home *** CD-ROM | disk | FTP | other *** search
/ GameStar 2004 April / Gamestar_61_2004-04_dvdb.iso / DVDStar / Editace / hltp.exe / {app} / Applications / QuArK / plugins / mapslide.py < prev    next >
Text File  |  2004-01-05  |  12KB  |  441 lines

  1.  
  2. ########################################################
  3. #
  4. #                          Slider Plugin
  5. #                          v2.0, Aug 2001
  6. #                      works with Quark 6.3        
  7. #
  8. #
  9. #                    by tiglari@planetquake.com     
  10. #
  11. #   You may freely distribute modified & extended versions of
  12. #   this plugin as long as you give due credit to tiglari &
  13. #   Armin Rigo. (It's free software, just like Quark itself.)
  14. #
  15. #   Please notify bugs & improvements to tiglari@hexenworld.com
  16. #
  17. ###
  18. ##########################################################
  19.  
  20. #$Header: /cvsroot/quark/runtime/plugins/mapslide.py,v 1.6 2003/12/17 13:58:59 peter-b Exp $
  21.  
  22.  
  23.  
  24. Info = {
  25.    "plug-in":       "Slide Plugin",
  26.    "desc":          "Sliding things along and around axes",
  27.    "date":          "10 Aug 2001",
  28.    "author":        "tiglari",
  29.    "author e-mail": "tiglari@planetquake.com",
  30.    "quark":         "Version 6.3" }
  31.  
  32.  
  33. import quarkx
  34. import quarkpy.mapmenus
  35. import quarkpy.mapentities
  36. import quarkpy.qmenu
  37. import quarkpy.mapeditor
  38. import quarkpy.mapcommands
  39. import quarkpy.mapoptions
  40. import quarkpy.maphandles
  41. import quarkpy.dlgclasses
  42. from tagging import *
  43. import mapmadsel   # to make mad selector load first
  44. from quarkpy.maputils import *
  45.  
  46. #
  47. #------------  WTF -----------
  48. #
  49. #  First we define the slider's dialog, then a Click function
  50. #   to bring it up when an appropriate menu-button, and finally
  51. #   redefine a bunch of menus to put the Click function on them.
  52. #  
  53.  
  54. #
  55. # see the dialogs in quarkpy.qeditor and plugins.mapsearch
  56. #  for commented basic dilaog code.  LiveEditDlg is a jazzed
  57. #  up descendent of quarkpy.qmacro.dialogbox, adapted for
  58. #  dialogs that are supposed to drive things around on the
  59. #  screen While U Watch.
  60. #
  61.  
  62. class SlideDlg (quarkpy.dlgclasses.LiveEditDlg):
  63.     #
  64.     # dialog layout
  65.     #
  66.  
  67.     endcolor = AQUA
  68.     size = (150,150)
  69.     dfsep = 0.40
  70.  
  71.     dlgdef = """
  72.         {
  73.         Style = "9"
  74.         Caption = "Slide Object Dialog"
  75.  
  76.         along: = 
  77.         {
  78.         Txt = "Along"
  79.         Typ = "EU"
  80.         Hint = "Movement Along Axis, offset from present position." $0D "  Increment 1 or gridstep"
  81.         }
  82.  
  83.         sep: = {Typ="S" Txt=" "} 
  84.         
  85.         around: = 
  86.         {
  87.         Txt = "Around"
  88.         Typ = "EU"
  89.         Hint = "Movement Around Axis, offset from preset position." $0D "  In degrees, regardless of grid" $0D "  But FORCE option will force to grid after movement."
  90.         }
  91.  
  92.         sep: = {Typ="S" Txt=" "} 
  93.         
  94.         force: =
  95.         {
  96.         Txt = "Grid"
  97.         Typ = "X"
  98.         Hint = "Object forced to grid after movement"
  99.         }
  100.  
  101.         sep: = { Typ="S" Txt=""}
  102.  
  103.         exit:py = {Txt="" }
  104.  
  105.     }
  106.     """
  107.  
  108. #
  109. # ---- Click Function
  110. #
  111.        
  112. #
  113. #    First an auxiliary:
  114. #
  115. # delta is the cumulative difference between starting and
  116. #  current positions.
  117. # diff is the difference from the last position.
  118. # if there is a gridstep, we want to round diff up or down
  119. #   to the gridstep, and revise delta accordingly
  120. #
  121. def gridify(delta, diff, gridstep):
  122. #    debug('diff %d, delta %d'%(diff,delta))
  123.     if not gridstep:
  124.             return delta
  125.     orig = delta-diff
  126.     (rem, quot) = math.modf(diff/gridstep)
  127. #    debug("%d, %d"%(rem,quot))
  128.     if diff < 0:
  129.         sign = -1
  130.     else:
  131.         sign = 1
  132.     if rem:
  133.     #    squawk("d: %s, r: %s"%(delta, quot+gridstep))
  134.         diff = (quot+sign)*gridstep
  135.     return orig+diff
  136.  
  137. #
  138. # Now for the click
  139. #
  140. def EdgeSlideClick(m):
  141.     editor = m.editor
  142.     edge = m.tagged
  143.     
  144.     #
  145.     # parameters are stuffed into the pack class/object,
  146.     #  and passed thereby
  147.     #
  148.     class pack:
  149.         "a place to stick stuff"
  150.     pack.edge = edge
  151.     pack.axis = (edge[0]-edge[1]).normalized
  152.     pack.o = m.o
  153.     pack.along = 0
  154.     pack.around = 0
  155.     #
  156.     # orig_object is the original, o will be replaced as
  157.     #  the dlg's changes are executed
  158.     #
  159.     pack.orig_object=pack.o
  160.       
  161.     #
  162.     # this initializes the dialog's values, via code in
  163.     #  dlgclasses.LiveEdit dialog that runs the function
  164.     #  passed as its `setup' parameter
  165.     #
  166.     # pack=pack below is a `closure', which effectively passes
  167.     #  some locally defined info to the function which is then
  168.     #  passed as an argument (similar to callbacks in effect,
  169.     #  but easier to use once you get used to it).
  170.     #
  171.     def setup(self, pack=pack):
  172.         src = self.src
  173.  
  174.         self.axis = set_sign(pack.axis)
  175.         self.pack = pack
  176.         self.point = pack.edge[0]
  177.         src["along"] = str(pack.along)
  178.         src["around"] = str(int(pack.around/deg2rad))
  179.       
  180.     #
  181.     # And here's the `action' function that gets called
  182.     #  every time you change the data in the dialog box.
  183.     #
  184.     def action(self, pack=pack, editor=m.editor):
  185.         src = self.src
  186.         delta = eval(src["along"]) # cumulative displacement from inital position
  187.         if delta != pack.along:
  188.             delta = gridify(delta, delta-pack.along, editor.gridstep)
  189.         pack.along=delta
  190.         phi = eval(src["around"])*deg2rad
  191.         new = pack.orig_object.copy()
  192.         matrix = ArbRotationMatrix(self.axis, phi)
  193.         new.linear(self.point, matrix)
  194.         new.translate(delta*self.axis)
  195.         if src["force"] and editor.gridstep:
  196.             new.forcetogrid(editor.gridstep)
  197.  
  198.         undo=quarkx.action()
  199.         undo.exchange(pack.o, new)
  200.         editor.ok(undo, "move object wrt axis")
  201.         #
  202.         # And this little trick is necessary to keep the undo
  203.         #  mechanism happy, each data change swaps in the newly
  204.         #  created object for the old pack.o, undo-ably.
  205.         #
  206.         pack.o = new
  207.         pack.along = delta
  208.         pack.around = phi
  209.  
  210.     #
  211.     # And finally call the dialog, with the functions we have
  212.     #  created as parameters, and also a label, 'axis slide',
  213.     #  to file the position of this dialog under between
  214.     #  uses (and across sessions).
  215.     #
  216.     SlideDlg(quarkx.clickform, 'axis_slide', editor, setup, action)
  217.     
  218.  
  219. #
  220. # And a whole new one for sliding things around over a tagged face
  221. #  (such as after executing snap object to tagged plane_
  222. #
  223.  
  224. class PlaneSlideDlg (quarkpy.dlgclasses.LiveEditDlg):
  225.     #
  226.     # dialog layout
  227.     #
  228.  
  229.     endcolor = AQUA
  230.     size = (150,160)
  231.     dfsep = 0.40
  232.  
  233.     dlgdef = """
  234.         {
  235.         Style = "9"
  236.         Caption = "Slide Object above Plane "
  237.  
  238.         away: = 
  239.         {
  240.         Txt = "Away"
  241.         Typ = "EU"
  242.         Hint = "Movement along the normal to plane, offset from present position." $0D "  Increment 1 or gridstep"
  243.         }
  244.  
  245.         sep: = {Typ="S" Txt=" "} 
  246.         
  247.         across: = 
  248.         {
  249.         Txt = "Across"
  250.         Typ = "EQ"
  251.         Hint = "Movement across the surface of the plane, offset from preset position."
  252.         }
  253.  
  254.         sep: = {Typ="S" Txt=" "} 
  255.         
  256.         force: =
  257.         {
  258.         Txt = "Force"
  259.         Typ = "X"
  260.         Hint = "Object forced to grid after movement"
  261.         }
  262.  
  263.         sep: = { Typ="S" Txt=""}
  264.  
  265.         exit:py = {Txt="" }
  266.  
  267.     }
  268.     """
  269.  
  270.  
  271. #
  272. # Now for the click
  273. #
  274. def PlaneSlideClick(m):
  275.     editor = m.editor
  276.     plane = m.tagged
  277.     
  278.     #
  279.     # parameters are stuffed into the pack class/object,
  280.     #  and passed thereby
  281.     #
  282.     class pack:
  283.         "a place to stick stuff"
  284.     pack.plane = plane
  285.     pack.normal = plane.normal
  286.     pack.o = m.o
  287.     pack.away = 0
  288.     pack.across = 0,0
  289.     pack.axes = bestaxes(pack.normal)
  290.     #
  291.     # orig_object is the original, o will be replaced as
  292.     #  the dlg's changes are executed
  293.     #
  294.     pack.orig_object=pack.o
  295.     
  296.     def setup(self, pack=pack):
  297.         src = self.src
  298.  
  299.         src["away"] = str(pack.away)
  300.         src["across"] = "%.1f %.1f"%pack.across
  301.       
  302.       
  303.     #
  304.     # And here's the `action' function that gets called
  305.     #  every time you change the data in the dialog box.
  306.     #
  307.     def action(self, pack=pack, editor=m.editor):
  308.         src = self.src
  309.         away = eval(src["away"]) # cumulative displacement from inital position
  310.  
  311.         def griddo(new, old, step=editor.gridstep):
  312.             if new != old:
  313.                 return gridify(new, new-old, step)
  314.             else:
  315.                 return new
  316.             
  317.         if away != pack.away:
  318.             away = gridify(away, away-pack.away, editor.gridstep)
  319.         pack.away=away
  320.         across = read2vec(src["across"])
  321.         across = tuple(map(griddo,across,pack.across))
  322.         new=pack.orig_object.copy()
  323.         new.translate(away*pack.normal+across[0]*pack.axes[0]+across[1]*pack.axes[1])
  324.         if src["force"] and editor.gridstep:
  325.             new.forcetogrid(editor.gridstep)
  326.  
  327.         undo=quarkx.action()
  328.         undo.exchange(pack.o, new)
  329.         editor.ok(undo, "move object wrt axis")
  330.         #
  331.         # And this little trick is necessary to keep the undo
  332.         #  mechanism happy, each data change swaps in the newly
  333.         #  created object for the old pack.o, undo-ably.
  334.         #
  335.         pack.o = new
  336.         pack.away = away
  337.         pack.across = across
  338.  
  339.     #
  340.     # And finally call the dialog, with the functions we have
  341.     #  created as parameters, and also a label, 'axis slide',
  342.     #  to file the position of this dialog under between
  343.     #  uses (and across sessions).
  344.     #
  345.     PlaneSlideDlg(quarkx.clickform, 'plane_slide', editor, setup, action)
  346.     
  347. #
  348. #  --- Now for the menus:
  349. #
  350.  
  351. types = {
  352.     ":e": "Entity",
  353.     ":g": "Group",
  354.     ":b": "Entity",
  355.     ":p": "Poly"}
  356.  
  357.  
  358. def slidePopup(o, editor):
  359.     label = types[o.type]
  360.     list = [makeEdgeSlide(o,editor),makePlaneSlide(o,editor)]
  361.     popup=qmenu.popup('Slide %s'%label,list,hint="|Slide %s along tagged edge or above tagged plane"%label)
  362.     return popup
  363.  
  364. #
  365. # returns the menu item, and disables it if appropriate
  366. #
  367. def makeEdgeSlide(o, editor):
  368.     label = types[o.type]
  369.     item = qmenu.item("along/around tagged edge",
  370.         EdgeSlideClick, "|Slides %s along or around tagged axis."%label.lower())
  371.     tagged = gettaggededge(editor)
  372.     if tagged is None:
  373.         item.state = qmenu.disabled
  374.         #
  375.         # Add some stuff to the disabler to explain why the item
  376.         #   is enabled.
  377.         #
  378.         item.hint = item.hint + "\n\nTag an edge in order to enable this menu item."
  379.     else:
  380.         item.o = o
  381.         item.editor=editor
  382.         item.tagged=tagged
  383.     return item
  384.  
  385.  
  386. def makePlaneSlide(o, editor):
  387.     label = types[o.type]
  388.     item = qmenu.item("above tagged plane",
  389.         PlaneSlideClick, "|Slides %s above tagged plane."%label.lower())
  390.     tagged = gettaggedplane(editor)
  391.     if tagged is None:
  392.         item.state = qmenu.disabled
  393.         #
  394.         # Add some stuff to the disabler to explain why the item
  395.         #   is enabled.
  396.         #
  397.         item.hint = item.hint + "\n\nTag an plane in order to enable this menu item."
  398.     else:
  399.        item.o = o
  400.        item.editor=editor
  401.        item.tagged=tagged
  402.     return item
  403.  
  404.  
  405. for Type in (quarkpy.mapentities.PolyhedronType,
  406.              quarkpy.mapentities.GroupType,
  407.              quarkpy.mapentities.BrushEntityType):
  408.              
  409.     def newmenu(o, editor, oldmenu=Type.menu.im_func):
  410.         menu = oldmenu(o, editor)
  411.         menu[:0] = [slidePopup(o,editor)]
  412.         return menu
  413.         
  414.     Type.menu = newmenu
  415.  
  416.  
  417. # ----------- REVISION HISTORY ------------
  418. #
  419. #
  420. # $Log: mapslide.py,v $
  421. # Revision 1.6  2003/12/17 13:58:59  peter-b
  422. # - Rewrote defines for setting Python version
  423. # - Removed back-compatibility with Python 1.5
  424. # - Removed reliance on external string library from Python scripts
  425. #
  426. # Revision 1.5  2001/08/11 02:53:02  tiglari
  427. # refurbish & add slide above tagged plane facility
  428. #
  429. # Revision 1.4  2001/06/17 21:10:56  tiglari
  430. # fix button captions
  431. #
  432. # Revision 1.3  2001/06/16 03:19:05  tiglari
  433. # add Txt="" to separators that need it
  434. #
  435. # Revision 1.2  2000/06/03 10:25:30  alexander
  436. # added cvs headers
  437. #
  438. #
  439. #
  440. #
  441.